"use client"; import AutoEssayWindow from "@/app/[language]/forum/components/AutoEssayWindow"; import FileUploading from "@/app/[language]/forum/components/FileUploadingWindow"; import TextView from "@/app/[language]/forum/components/TextView"; import useGetEssay from "@/app/[language]/forum/query/useGetEssay"; import useGetForum from "@/app/[language]/forum/query/useGetForum"; import usePostAutoEssay from "@/app/[language]/forum/query/usePostAutoEssay"; import usePostEssay from "@/app/[language]/forum/query/usePostEssay"; import usePostFile from "@/app/[language]/forum/query/usePostFile"; import usePutAutoEssay from "@/app/[language]/forum/query/usePutAutoEssay"; import usePutEssay from "@/app/[language]/forum/query/usePutEssay"; import { useForumStore, useTaehuiStore } from "@/state/Stores"; import { useQueryClient } from "@tanstack/react-query"; import { observer } from "mobx-react-lite"; import { useTranslations } from "next-intl"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { PencilFill } from "react-bootstrap-icons"; import Button from "react-bootstrap/Button"; import CloseButton from "react-bootstrap/CloseButton"; import Col from "react-bootstrap/Col"; import FormCheck from "react-bootstrap/FormCheck"; import FormControl from "react-bootstrap/FormControl"; import InputGroup from "react-bootstrap/InputGroup"; import InputGroupText from "react-bootstrap/InputGroupText"; import Row from "react-bootstrap/Row"; import Stack from "react-bootstrap/Stack"; import { toast } from "react-toastify"; import { useWindowArea } from "taehui-lib/fe-utilities"; export default observer(() => { const { isTitleTextFilled, title, text, viewUnit, setTitle, setText, autoEssayID, setAutoEssayID, setAutoEssayOpened, } = useForumStore(); const inputView = useRef<HTMLButtonElement>(null); const { taehuiAvatarID, taehuiAvatarName } = useTaehuiStore(); const t = useTranslations(); const [isTestMode, setTestMode] = useState(false); const [testText, setTestText] = useState(""); const { forumID, essayID: [essayID] = [""] } = useParams<{ forumID: string; essayID: string[]; }>(); const textView = useRef<HTMLTextAreaElement>(null); const [textViewHeight, setTextViewHeight] = useState(""); const { windowLength, windowHeight } = useWindowArea(); useLayoutEffect(() => { const textViewArea = textView.current?.getBoundingClientRect(); const inputViewArea = inputView.current?.getBoundingClientRect(); setTextViewHeight( `${windowHeight - 8 * 4 - ((textViewArea?.top ?? 0) + (inputViewArea?.height ?? 0))}px`, ); }, [windowLength, windowHeight]); const { mutateAsync: postAutoEssay } = usePostAutoEssay(); const { mutateAsync: putAutoEssay } = usePutAutoEssay(); const { mutateAsync: putEssay } = usePutEssay(); const { mutateAsync: postFile, isPending: isPostFilePending } = usePostFile(); const { mutateAsync: postEssay } = usePostEssay(); const { data: essay, isFetched: isEssayLoaded } = useGetEssay(essayID); const searchParams = useSearchParams(); const { push } = useRouter(); useEffect(() => { if (isEssayLoaded) { setTitle(essay.title); setText(essay.text); } }, [essay, isEssayLoaded, setText, setTitle]); const setTag = (tag: string) => { const { current } = textView; if (current) { const { selectionStart, selectionEnd } = current; const t = text.substring(selectionStart, selectionEnd); const tag0 = "<" + tag + ">"; const tag1 = "</" + tag + ">"; if (t.startsWith(tag0) || t.endsWith(tag1)) { setText( text.substring(0, selectionStart) + text.substring( selectionStart + tag0.length, selectionStart + t.length - tag1.length, ) + text.substring(selectionEnd), ); setTimeout(() => { current.selectionStart = selectionStart; current.selectionEnd = selectionEnd - tag0.length - tag1.length; current.focus(); }, 0); } else { setText( text.substring(0, selectionStart) + tag0 + text.substring(selectionStart, selectionEnd) + tag1 + text.substring(selectionEnd), ); setTimeout(() => { current.selectionStart = selectionStart; current.selectionEnd = selectionEnd + tag0.length + tag1.length; current.focus(); }, 0); } } }; useEffect(() => { if (!essayID) { setTitle(""); setText(""); } }, [essayID, setText, setTitle]); useEffect(() => { const postAutoEssaysID = setInterval(async () => { if (isTitleTextFilled) { if (typeof autoEssayID === "number") { await putAutoEssay({ autoEssayID, title, text }); } else { const { autoEssayID } = await postAutoEssay({ forumID, title, text, }); setAutoEssayID(autoEssayID); } } }, 60000); return () => { clearInterval(postAutoEssaysID); }; }, [ autoEssayID, forumID, isTitleTextFilled, postAutoEssay, putAutoEssay, setAutoEssayID, t, text, title, ]); useLayoutEffect(() => { if (isTestMode) { setTestText(text); } }, [isTestMode, text]); const { data: { title: forumTitle }, } = useGetForum( forumID, Number.parseInt(searchParams.get("page") ?? "1"), viewUnit, ); const onFileUpload = () => { const inputElement = document.createElement("input"); inputElement.type = "file"; inputElement.accept = "audio/*,image/*,video/*"; inputElement.addEventListener("change", async ({ target }) => { const file = (target as HTMLInputElement).files?.[0]; const { current } = textView; if (file && current) { const text = await postFile({ file, textView: current, }); if (text) { setText(text); } } }); inputElement.click(); }; const { back } = useRouter(); const queryClient = useQueryClient(); return ( <> <Stack gap={2}> <Row> <Col> <h4>{forumTitle}</h4> </Col> <Col xs="auto"> <FormCheck id="isTestMode" label={t("setTestMode")} checked={isTestMode} onChange={() => { setTestMode((prevState) => !prevState); }} /> </Col> <Col xs="auto"> <CloseButton onClick={back} /> </Col> </Row> <hr /> {isTestMode ? ( <> <TextView title={title} text={testText} avatarID={taehuiAvatarID} avatarName={taehuiAvatarName} /> <hr /> </> ) : ( <> <InputGroup> <InputGroupText>{t("title")}</InputGroupText> <FormControl type="text" isInvalid={!title} isValid={!!title} placeholder={t("title")} value={title} onChange={({ target: { value } }) => { setTitle(value); }} /> <Button onClick={async () => { await queryClient.invalidateQueries({ queryKey: ["autoEssay"], }); setAutoEssayOpened(true); }} > {t("autoEssays")} </Button> </InputGroup> <Stack gap={2} direction="horizontal"> <Button variant="outline-primary" onClick={onFileUpload}> {t("fileUpload")} </Button> <Button variant="outline-primary" onClick={() => { setTag("strong"); }} > <strong>{t("textTag")}</strong> </Button> <Button variant="outline-primary" onClick={() => { setTag("i"); }} > <i>{t("textTag")}</i> </Button> <Button variant="outline-primary" onClick={() => { setTag("u"); }} > <u>{t("textTag")}</u> </Button> <Button variant="outline-primary" onClick={() => { setTag("s"); }} > <s>{t("textTag")}</s> </Button> </Stack> <FormControl ref={textView} style={{ height: textViewHeight }} as="textarea" isInvalid={!text} isValid={!!text} placeholder={t("text")} value={text} onChange={({ target: { value } }) => { setText(value); }} /> </> )} {essayID ? ( <Button ref={inputView} variant="warning" onClick={async () => { if (title && text) { await putEssay({ essayID, title, text }); push(`/forum/${forumID}/${essayID}`); } else { toast.error(t("failedValidation")); } }} > <PencilFill /> </Button> ) : ( <Button ref={inputView} variant="success" onClick={async () => { if (title && text) { const { essayID } = await postEssay({ forumID, title, text, }); push(`/forum/${forumID}/${essayID}`); } else { toast.error(t("failedValidation")); } }} > <PencilFill /> </Button> )} </Stack> <FileUploading isOpened={isPostFilePending} /> <AutoEssayWindow /> </> ); });